package com.dotflat.fin.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.dotflat.fin.domain.FinPayAccount;
import com.dotflat.fin.domain.FinPlanOrder;
import com.dotflat.fin.domain.PayRecord;
import com.dotflat.fin.dto.AliPayNotifyParam;
import com.dotflat.fin.dto.AppPayResult;

import com.dotflat.fin.model.APPPayAttach;
import com.dotflat.fin.repository.PayRecordRepository;
import com.dotflat.fin.service.AliPayService;
import com.dotflat.fin.service.FinPayAccountService;
import com.google.common.collect.ImmutableMap;
import com.matt.commons.exception.BusinessException;
import com.matt.domain.Clientele;
import com.matt.foundation.model.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import static com.dotflat.fin.cnst.Const.*;
import static com.dotflat.fin.cnst.PayConst.*;


/**
 * 微信支付
 * @author ：Ron
 * @date ：Created in 2020/3/17 20:26
 */
@Service
@Slf4j
public class AliPayServiceImpl implements AliPayService {

    @Autowired
    private PayRecordRepository payRecordRepository;

    @Autowired
    private PayServiceProxy payServiceProxy;

    @Autowired
    private FinPayAccountService accountService;

    @Value("${ali-pay.app.appid}")
    private String appId;

    @Value("${ali-pay.app.app_private_key}")
    private String appPrivateKey;

    @Value("${ali-pay.app.app_public_key}")
    private String appPublicKey;

    @Value("${ali-pay.app.public_key}")
    private String publicKey;

    @Value("${ali-pay.app.notify_url}")
    private String notify_url;

    private AlipayClient areaAliPayClient(PayRecord payRecord){
        FinPayAccount areaAccount = accountService.activedPayAccount(payRecord.getAreaId(), 1);

        AlipayClient alipayClient = null;
        if (areaAccount != null){
            payRecord.setPayAccountId(areaAccount.getId());

            alipayClient = new DefaultAlipayClient(ALIPAY_SERVER_URL, areaAccount.getAppId(),
                    areaAccount.getAliAppPrivateKey(), PAY_FORMAT, PAY_CHARSET, areaAccount.getAliPublicKey(), PAY_SIGN_TYPE);
        }else{
            alipayClient = new DefaultAlipayClient(ALIPAY_SERVER_URL, appId,
                    appPrivateKey, PAY_FORMAT, PAY_CHARSET, publicKey, PAY_SIGN_TYPE);
        }

        return alipayClient;
    }

    @Override
    public Map<String, Object> prepare(Clientele clientele, String openId, Integer resourceType, FinPlanOrder finPlanOrder, long useDeposit) {


        //创建本地订单
//        ====================================================
        PayRecord payRecord = new PayRecord();
        payRecord.setPayOrderId(finPlanOrder.getId());
        payRecord.setCreatedBy(clientele.getId());
        payRecord.setAreaId(clientele.getAreaId());

        payRecordRepository.save(payRecord);

        String outTradeNo = payRecord.getId();

        Long amount = finPlanOrder.getTotalAmount() - useDeposit;  //减掉从定金里面扣的钱

        //实例化客户端,参数待修改
        AlipayClient alipayClient = areaAliPayClient(payRecord);

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("支付帐单款项");   //内容
        model.setSubject(finPlanOrder.getTitle()); //标题
        model.setOutTradeNo(outTradeNo);  //订单号
        model.setTimeoutExpress("30m"); //
        model.setPassbackParams(JSON.toJSONString(new APPPayAttach(resourceType,  finPlanOrder.getId())));

        long yuan = amount / 100;
        long fen = amount % 100;
        String totalAmount = String.format("%d.%02d", yuan, fen);
        model.setTotalAmount(totalAmount);
        request.setBizModel(model);
        request.setNotifyUrl(notify_url);

        //这里和普通的接口调用不同，使用的是sdkExecute
        AlipayTradeAppPayResponse response = null;
        try {
            response = alipayClient.sdkExecute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        if(response.isSuccess()){
            log.debug(response.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。

            String res = response.getBody();

            if (StringUtils.isEmpty(res)){
                throw new BusinessException(ErrorCode.SIGN_ERROR, "获取支付宝订单失败");
            }

            return ImmutableMap.of("out_trade_no", outTradeNo, "res", res);
        }

        throw new BusinessException(ErrorCode.SIGN_ERROR, "获取支付宝订单失败");
    }

    @Override
    public Map<String,Object> prepare(Clientele clientele, String openId, Integer resourceType, String resourceId, long useDeposit){
        PayRecord payRecord = new PayRecord();
//        payRecord.setPayOrderId(finPlanOrder.getId());
        payRecord.setCreatedBy(clientele.getId());
        payRecord.setAreaId(clientele.getAreaId());

        payRecordRepository.save(payRecord);

        String outTradeNo = payRecord.getId();

        String title = "客户自定义服务购买";
        Long amount = PHOTO_ALBUM_PRINT_PRICE - useDeposit;  //减掉从定金里面扣的钱

        //实例化客户端,参数待修改
        AlipayClient alipayClient = areaAliPayClient(payRecord);

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("支付帐单款项");   //内容
        model.setSubject(title); //标题
        model.setOutTradeNo(outTradeNo);  //订单号
        model.setTimeoutExpress("30m"); //
        model.setPassbackParams(JSON.toJSONString(new APPPayAttach(resourceType,  resourceId)));

        long yuan = amount / 100;
        long fen = amount % 100;
        String totalAmount = String.format("%d.%02d", yuan, fen);
        model.setTotalAmount(totalAmount);
        request.setBizModel(model);
        request.setNotifyUrl(notify_url);

        //这里和普通的接口调用不同，使用的是sdkExecute
        AlipayTradeAppPayResponse response = null;
        try {
            response = alipayClient.sdkExecute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        if(response.isSuccess()){
            log.debug(response.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。

            String res = response.getBody();

            if (StringUtils.isEmpty(res)){
                throw new BusinessException(ErrorCode.SIGN_ERROR, "获取支付宝订单失败");
            }

            return ImmutableMap.of("out_trade_no", outTradeNo, "res", res);
        }

        throw new BusinessException(ErrorCode.SIGN_ERROR, "获取支付宝订单失败");
    }

    @Transactional(rollbackOn = Exception.class)
    public boolean processAliPayOrderResult(Map notifyMap) throws AlipayApiException {
        //切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)

        boolean signPass = AlipaySignature.rsaCheckV1(notifyMap,
                publicKey, //这里是支付宝公钥不是应用公钥
                PAY_CHARSET, PAY_SIGN_TYPE);

        if (!signPass){
            log.error("签名校验失败");
            return signPass;
        }

        if (notifyMap == null){
            notifyMap = new HashMap<>();
        }

        return processAliPayOrderResultWithoutSign(notifyMap);
    }


    @Transactional(rollbackOn = Exception.class)
    public boolean processAliPayOrderResultWithoutSign(Map notifyMap) throws AlipayApiException {

        String json = JSON.toJSONString(notifyMap);
        log.debug(json);

        AliPayNotifyParam notifyParam = JSON.parseObject(json, AliPayNotifyParam.class);

        String trade_status = notifyParam.getTradeStatus();
        // 支付成功
        if (TRADE_SUCCESS.equals(trade_status) || TRADE_FINISHED.equals(trade_status) ) {
            // 处理支付成功逻辑
            try {
                long tf = (new BigDecimal(notifyParam.getReceiptAmount()).multiply(new BigDecimal("100"))).longValue();

                AppPayResult payResult = AppPayResult.of(notifyParam);

                APPPayAttach p = JSON.parseObject(payResult.getAttach(), APPPayAttach.class);

                if (p != null) {
                    payServiceProxy.processContractPayment("alipay",p, payResult.getTransactionId(), payResult.getOutTradeNo(), tf, payResult );
                } else {
                    log.error("::::not pay attach found::::");
                }
            } catch (Exception e) {
                log.error("支付宝回调业务处理报错,params:" + notifyMap, e);
            }
        } else {
            log.error("没有处理支付宝回调业务，支付宝交易状态：{},params:{}", trade_status, notifyMap);
        }

        return true;
    }

    public void check(Integer resourceType, String outTradeNo){

        AlipayClient alipayClient = new DefaultAlipayClient(ALIPAY_SERVER_URL, appId,
                appPrivateKey, PAY_FORMAT, PAY_CHARSET, publicKey, PAY_SIGN_TYPE);


        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();

        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setOutTradeNo(outTradeNo);  //订单号

        request.setBizModel(model);

        AlipayTradeQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
            String body = response.getBody();

            Map result = JSON.parseObject(body);
            Map notifyMap = (Map) result.get("alipay_trade_query_response");
            processAliPayOrderResultWithoutSign(notifyMap);

        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.SIGN_ERROR, e.getErrMsg());
        }
    }
}
